"use strict";
/**
 * 大部分编译器运行时分为三个主要的：解析，转换和代码生成
 * 解析：把源码转换成抽象语法树
 * 转换：对抽象语法树进行操作
 * 代码生成：把转换过后的代码转换成目标代码
 */

//词法分析(add 2 (subtract 4 2))   =>   [{ type: 'paren', value: '(' }, ...]
function tokenizer(input) {
  let current = 0; //跟踪当前遍历的位置
  let tokens = [];

  while (current < input.length) {
    let char = input[current];
    if (char === "(") {
      tokens.push({
        type: "paren",
        value: "(",
      });
      current++;
      continue;
    }

    if (char === ")") {
      tokens.push({
        type: "paren",
        value: ")",
      });
      current++;
      continue;
    }

    let WHITESPACE = /\s/;

    if (WHITESPACE.test(char)) {
      current++;
      continue;
    }

    let NUMBERS = /[0-9]/

    if(NUMBERS.test(char)){
        let value = ''
        while(NUMBERS.test(char)){
            value += char
            char = input[++current]
        }
        tokens.push({type:'number',value})
        continue
    }

    if (char === '"') {
      let value = "";
      char = input[++current];
      while (char !== '"') {
        value += char;
        char = input[++current];
      }
      char = input[++current];
      tokens.push({ type: "string", value });
      continue;
    }
    let LETTERS = /[a-z]/i;
    if (LETTERS.test(char)) {
      let value = "";
      while (LETTERS.test(char)) {
        value += char;
        char = input[++current];
      }
      tokens.push({ type: "name", value });
      continue;
    }
    throw new TypeError("I dont konw what this character is:" + char);
  }

  return tokens;
}

function parser(tokens) {
  let current = 0;

  function walk() {
    let token = tokens[current];
    if (token.type === "number") {
      current++;
      return {
        type: "NumberLiteral",
        value: token.value,
      };
    }
    if(token.type === "string"){
        current++
        return {
            type:"StringLiteral",
            value:token.value
        }
    }
    if(token.type == "paren" && token.value === "("){
        token = tokens[++current]
        let node = {
            type:"CallExpression",
            name:token.value,
            params:[]
        }
        token = tokens[++current]
        while((token.type !== "paren") || (token.type === "paren" && token.value !== ")")){
            node.params.push(walk())
            token = tokens[current]
        }
        current++
        return node
    }
    throw new TypeError(token.type)
  }

  let ast = {
    type:"Program",
    body:[],
  }
   while(current < tokens.length){
    ast.body.push(walk())
   }

  return ast;
}

function traverser(ast, visitor) {
    function tranverseArray(array,parent){
        array.forEach(child => {
            traverseNode(child,parent)
        })
    }
    function traverseNode(node,parent){
        let methods = visitor[node.type]
        if(methods && visitor[node.type]){
            methods.enter(node,parent)
        }
        switch(node.type){
            case "Program":
                tranverseArray(node.body,node)

                break
            case "CallExpression":
                tranverseArray(node.params,node)
                break
            case "NumberLiteral":
            
            case "StringLiteral":
                break
            
            default:
                throw new TypeError(node.type)
        }
        if(methods && methods.exit){
            methods.exit(node,parent)
        }
    }
    traverseNode(ast,null)

}

function transformer(ast) {
    let newAst = {
        type:'Program',
        body:[]
    }
    ast._context = newAst.body
    traverser(ast,{
        NumberLiteral:{
            enter(node,parent){
                parent._context.push({
                    type:'NumberLiteral',
                    value:node.value
                })
            }
        },
        StringLiteral:{
            enter(node,parent){
                parent._context.push({
                    type:"StringLiteral",
                    value:node.value
                })
            }
        },
        CallExpression:{
            enter(node,parent){
                let expression = {
                    type:"CallExpression",
                    callee:{
                        type:"Identifier",
                        name:node.name
                    },
                    arguments:[]
                }
                node._context = expression.arguments
                if(parent.type !== "CallExpression"){
                    expression = {
                        type:"ExpressionStatement",
                        expression:expression
                    }
                }
                parent._context.push(expression)
            }
        }
    })
    return newAst
}

function codeGenerator(node) {
    switch(node.type){
        case "Program":
            return node.body.map(codeGenerator).join("\n")
        case "ExpressionStatement":
            return codeGenerator(node.expression) + ";"
        case "CallExpression":
            return (codeGenerator(node.callee) + 
            "(" +
             node.arguments.map(codeGenerator).join(", ")) + 
             ")"
        case "Identifier":
            return node.name
        case "NumberLiteral":
            return node.value
        case "StringLiteral":
            return '"' + node.value + '"'
        default:
            throw new TypeError(node.type)
    }
}

//编译器
function compiler(input) {
  let tokens = tokenizer(input); //词法分析，生成数组序列
  console.log("tokens:",tokens)
  let ast = parser(tokens); //语法分析，通过词法数组序列生成抽象语法树
  console.log("ast:",ast)
  let newAst = transformer(ast); //对抽象语法树进行转换，生成新的抽象语法树
  console.log("newAst:",newAst)
  let output = codeGenerator(newAst); //通过新的抽象语法树生成目标代码
  console.log("output:",output) 
  return output;
}

function test(){
    let input = "(add 2 (subtract 4 2))"
    compiler(input)
  
}
test()



module.exports = {
  tokenizer,
  parser,
  traverser,
  transformer,
  codeGenerator,
  compiler,
};
